<?php
/*  vim:ts=4:expandtab:cindent
 *  This file is part of Urd.
 *
 *  Urd is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *  Urd is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program. See the file "COPYING". If it does not
 *  exist, see <http://www.gnu.org/licenses/>.
 *
 * $LastChangedDate: 2008-07-18 19:15:03 +0200 (Fri, 18 Jul 2008) $
 * $Rev: 1304 $
 * $Author: gavinspearhead $
 * $Id: nntp.class.php 1304 2008-07-18 17:15:03Z gavinspearhead $
 */

/*
 * This file is based on Daniel Eiland's nzb.class.php:
 * 
 * 5 feb 2005
 * NZB Generator (c) 2005
 * Written by Daniel Eiland daniel@bokko.nl
 */

// This is an include-only file:
if (!defined('ORIGINAL_PAGE')) die('This file cannot be accessed directly.');

$pathnzb = realpath(dirname(__FILE__));


require_once "$pathnzb/../config.php";
require_once "$pathnzb/defines.php";
require_once "$pathnzb/functions.php";
//require_once "$pathnzb/urd_log.php";



class URD_NNTP {
	private $server;           // server name
	private $port;             // server port
	private $maxMssgs;         // fetch this amount of messages at the time
	private $downloadspeedArr; // is used to keep track of avg download speeds
	private $nntp;             // the nntp server connection
	private $db;               // the database to use
	private $timeout;	   // the timeout used for socket connections

	private $groupID;          // used to temporarily store the groupID across functions

	private $xoverformat;      // format of xover messages
	private $xover_number;     // store location of fields
	private $xover_subject;
	private $xover_from;
	private $xover_date;
	private $xover_messageid;
	private $xover_bytes;
	private	$xover_lines;
	private $xover_xref;


	function __destruct () 
    {
        echo_debug_function(DEBUG_NNTP, __FUNCTION__);
        $this->disconnect();
    }
	function disconnect() { 
		echo_debug_function(DEBUG_NNTP, __FUNCTION__);
		if ($this->nntp !== NULL && ($this->nntp instanceof NNTP_Client)) {
			$this->nntp->disconnect(); 
			$this->nntp = NULL;
		}
	}
	function is_connected()
	{
		return is_a($this->nntp,'NNTP_Client') &&  $this->nntp->is_connected();
	}
	function __construct (DatabaseConnection $db, $server, $connection_type=NULL, $port=0, $timeout=DEFAULT_SOCKET_TIMEOUT) 
	{
		assert (is_numeric($port) && is_numeric($timeout));
		echo_debug_function(DEBUG_NNTP, __FUNCTION__);
		//configuration
		global $config;
		//initialize some stuff
		$this->db = $db;
		$this->nntp = NULL;
		$this->downloadspeedArr = array();
		$conn = strtolower($connection_type); 
		switch ($conn) {
		default:
			write_log("Unknown encryption type: $connection_type", LOG_NOTICE);
			// fall through
		case NULL :
		case 'tcp':
		case 'off': 
			$conn = FALSE; 
			break;
		case 'tls':
		case 'ssl': 
			if (extension_loaded('openssl')) 
				$timeout = NULL; // quickfix for problem in stream_select
			else {
				write_log('SSL module not loaded', LOG_ERR);
				throw new exception('SSL module not loaded', ERR_NNTP_CONNECT_FAILED);
			}	
			break;
		}

		$this->server = $server;
		$this->timeout = $timeout;
		$this->connection = $conn;
		if ($port === NULL || $port == 0 || $port > 65535 || !is_numeric($port)) {
			$this->port = ($conn === FALSE) ? NNTP_PROTOCOL_CLIENT_DEFAULT_PORT : NNTP_SSL_PROTOCOL_CLIENT_DEFAULT_PORT;
		} else 
			$this->port = (int) $port;
		$this->maxMssgs = $config['maxmsgs']; //fetch this ammount of messages at the time

	}
	function connect($auth, $username='', $password='') 
	{// return true if posting is allowed, false if not
		assert (is_bool($auth));
		echo_debug_function(DEBUG_NNTP, __FUNCTION__);
		//connect to server
		if ($this->connection !== FALSE)
			$conn = $this->connection . ':';
		else 
			$conn = '';
		write_log ("Connecting to NNTP server: $conn{$this->server}:{$this->port}", LOG_INFO);
		try {
			$this->nntp = new NNTP_Client();
			$posting = $this->nntp->connect($this->server, $this->connection, $this->port, $this->timeout); 
		} catch (exception $e) {
			$this->nntp = NULL;
			write_log("Cannot connect to server: {$this->server}", LOG_ERR);
			write_log($e->getmessage() . " ({$e->getCode()})", LOG_ERR);
			throw new exception('Cannot connect to server', ERR_NNTP_CONNECT_FAILED);
		}

		if ($auth === TRUE) {
			try {
				write_log("using authentication as $username", LOG_INFO);
				$this->nntp->authenticate($username, $password);

			} catch (exception $e) {
				$this->nntp->disconnect();
				write_log('Cannot authenticate to server', LOG_ERR);
				write_log($e->getmessage(), LOG_ERR);
				throw new exception('Cannot authenticate to server', ERR_NNTP_CONNECT_FAILED);
			}
        }
        return $posting;
	}
	function get_first_last_group($groupId)
	{
		echo_debug_function(DEBUG_NNTP, __FUNCTION__);
		if (!is_numeric($groupId))
			throw new exception('Invalid command', ERR_INTERNAL_ERROR);
        $groupArr = get_group_info($this->db, $groupId);
        $code = 0;
		try {
			$data = $this->nntp->select_group($groupArr['name'], $code);
		} catch (exception $e) {
			throw new exception("Could not select group: $groupId}", ERR_GROUP_NOT_FOUND);
		}

		$last = $origlast = $data['last'];
		if($groupArr['last_record'] == 0) 
			$first = $data['first'];
		else 
			$first = $groupArr['last_record'] + 1;

		//calculate total number op parts
        $total = gmp_div($last, $first);
		return array($first, $last, gmp_strval($total));
	}
	function get_header($msg_id)
	{
		try {
			return $this->nntp->get_header($msg_id, FALSE);
		} catch (exception $e) {
			$code = $e->getCode();
			if (!$this->is_connected()) 
				throw new exception ("Connection lost ($code)", NNTP_NOT_CONNECTED_ERROR);
			else if ($code == '')
				$err_code = UNKNOWN_ERROR;
			else 
				$err_code = $code;
			throw new exception("Article not found ($err_code)", ERR_ARTICLE_NOT_FOUND);
		}
    }
 /*   function get_article_adv($msg_id, article_writer &$aw)
	{
		try {
			return $this->nntp->get_body_adv($msg_id, $aw);
		} catch (exception $e) {
			$code = $e->getCode();
			if (!$this->is_connected()) 
				throw new exception ("Connection lost ($code)", NNTP_NOT_CONNECTED_ERROR);
			else if ($code == '')
				$err_code = UNKNOWN_ERROR;
			else 
				$err_code = $code;
			throw new exception("Article not found ($err_code)", ERR_ARTICLE_NOT_FOUND);
		}
    }*/

	function get_article($msg_id)
	{
		try {
			return $this->nntp->get_body($msg_id, FALSE);
		} catch (exception $e) {
			$code = $e->getCode();
			if (!$this->is_connected()) 
				throw new exception ("Connection lost ($code)", NNTP_NOT_CONNECTED_ERROR);
			else if ($code == '')
				$err_code = UNKNOWN_ERROR;
			else 
				$err_code = $code;
			throw new exception("Article not found ($err_code)", ERR_ARTICLE_NOT_FOUND);
		}
	}

    // new edition using gmp
	private function get_headers(array $groupArr, $orig_start, $orig_stop, action $item, $mindate, $total, $done, $update_last_updated, $total_max, $total_counter, $total_correction, $compressed_headers)
	{
	//	assert(is_numeric($orig_start) && is_numeric($orig_stop) && is_numeric($mindate) && is_numeric($total) && is_numeric($done));
		// Download headers, to update local info
		echo_debug_function(DEBUG_NNTP, __FUNCTION__);
		if (gmp_cmp($orig_start, $orig_stop) > 0) {
			write_log('Odd values: ' . gmp_strval($orig_start) .  ', ', gmp_strval($orig_stop), LOG_NOTICE);
			// need to do 0-stop and start to maxint???
		}
		$groupID = $groupArr['ID'];
		$this->groupID = $groupID;

        echo_debug('Total max:' . gmp_strval($total_max), DEBUG_MAIN);
		// Store the xover message syntax in the class variable, used by ParseMessages:
		$this->get_overview_format();

		// Set msgto in case we have 0 articles to download:
		//$msgto = gmp_init(0);
		$dbid = $item->get_dbid();

		// Loop as we have a maximum number of articles we download per batch:
		$stop = $orig_stop;
		$start = gmp_max($orig_start, gmp_sub($stop, $this->maxMssgs)) ;
		echo_debug('Getting articles '.  gmp_strval($orig_start) . ' - ' . gmp_strval($orig_stop), DEBUG_MAIN);
        $older_counter = 0;
        if (gmp_cmp($orig_start, 0) == 0 && gmp_cmp($orig_stop, 0) == 0)
            return 0;
        $older_top = gmp_min(MAX_OLDER_COUNTER, (gmp_add(1000, gmp_div($total, 20))));// if 5% is older in one go we quit adding articles + 1000 articles for small ngs
		do {
			$starttime = microtime(TRUE);
			// Easy to use start/stop variables:

			// Get the batch of messages and store them in $msgs
            try {
                $msgs = $this->nntp->get_fast_overview(gmp_strval($start), gmp_strval($stop));
                // if we use XZver we need to deyenc it and inflate it to get the real data
                if ($compressed_headers) {
                    // yyencode
                    $data = deyenc($this->db, $msgs);
                    // deflate
                    $data = gzinflate($data);
                    if ($data === FALSE)
                        throw new exception('No data received');
                    $msgs = explode ("\n", $data);
                }
            } catch (exception $e) {
                $this->disconnect();
				write_log('Cannot get messages:', LOG_WARNING);
				write_log("{$e->getmessage()} ({$e->getcode()})", LOG_ERR);
				throw $e;
			}
            echo_debug('Total: ' . gmp_strval($total) . "; older: $older_counter; older_top: " . gmp_strval($older_top), DEBUG_MAIN);
			// Parse and process each message
			if (empty($msgs)) {
				write_log('Skipping:', LOG_INFO);
			} else {
				// check mindate otherwise return
                $rv = $this->parse_messages($msgs, $mindate, $older_counter);
                $total_counter = gmp_add($total_counter, count($msgs));
				if (gmp_cmp($older_counter, $older_top) > 0) {
					echo_debug('too old', DEBUG_MAIN);
					break;
				}
            }
            $ostop = gmp_strval($orig_stop);
            $ostart = gmp_strval($orig_start);
            $s = gmp_strval($start);

			if ($update_last_updated === TRUE)
				$query = "UPDATE groups SET \"last_record\" = '$ostop', \"first_record\" = '$s', \"mid_record\"= '$ostart' WHERE \"ID\" = $groupID"; // /lazy last_record need only the first time
			else 
				$query = "UPDATE groups SET \"first_record\" = '$s', \"mid_record\"='0' WHERE \"ID\" = $groupID"; // /lazy last_record need only the first time
			$res = $this->db->execute_query($query);

			// Determine download speed & ETA
			$stoptime = microtime(TRUE);
            $timeneeded = $stoptime - $starttime;
            $d1 = gmp_sub($orig_stop, gmp_add($start, $done));
            $d2 = (gmp_cmp($total_max, 0) > 0) ? gmp_min($total_max, $total) : $total;
			$this->store_ETA_info($timeneeded, $this->maxMssgs, gmp_strval($d1), gmp_strval($d2), $dbid); // inaccurate
			$stop = gmp_max($orig_start, gmp_sub($start, 1));
            $start = gmp_max($orig_start, gmp_sub($start, $this->maxMssgs));
            echo_debug('Total max: ' . gmp_strval($total_max) . ', counter: ' . gmp_strval($total_counter), DEBUG_MAIN);
		} while (gmp_cmp($start, $stop) < 0 && (gmp_cmp($total_max, 0) == 0 || gmp_cmp($total_counter, $total_max) <= 0));

        // Update group table with update time:
        $o = gmp_strval($orig_stop);
        $query = "UPDATE groups SET \"last_updated\" = '" . time() . "' , \"last_record\" = '$o' WHERE \"ID\" = $groupID";
        $res = $this->db->execute_query($query);
        return $total_counter;
	}

// new version using gmp
	function update_newsgroup(array $groupArr, action $item)
    {// todo add 3rd record identifier...; first update gets min(begin, lowmark) to midmark; 2nd gets higmark to end
        global $config;
		echo_debug_function(DEBUG_NNTP, __FUNCTION__);
		$first = $last = gmp_init(0);
		$dbid = $item->get_dbid();
        $groupID = $groupArr['ID'];
        $continue_old = FALSE;
		$group_name = $groupArr['name'];
        $total_max = gmp_init(get_config($this->db, 'total_max_articles'));
        $compressed_headers = $groupArr['compressed_headers'];
		echo_debug('Total max: ' . gmp_strval($total_max), DEBUG_MAIN);
		// convert to epoctime:
        try { 
            $this->nntp->set_compressed_headers($compressed_headers);
			update_queue_status ($this->db, $dbid, NULL, NULL, 0, 'Finding first valid article');
			write_log("Selecting group: $group_name", LOG_NOTICE);
			try {
				$data = $this->nntp->select_group($group_name);
			} catch (exception $e) {
				throw new exception("Could't select group $group_name", ERR_GROUP_NOT_FOUND);
			}
			$expire = $groupArr['expire'];
			$mindate = time() - ($expire * 24 * 3600);
			$m1 = gmp_init($groupArr['first_record']);
			$m2 = gmp_init($groupArr['mid_record']);
            $m3 = gmp_init($groupArr['last_record']);
            if (gmp_cmp($m1, 0) ==0 && gmp_cmp($m2, 0) == 0 && gmp_cmp($m3, 0) ==0 && $groupArr['retention'] > 0) {
                $total_correction = max (1, $expire / $groupArr['retention']);
            } else
                $total_correction = 1 ;

			$first = gmp_init($data['first']);
			$last = gmp_add(gmp_init($data['last']), 1);
			$last1 = $first1 = $last2 = $first2 = gmp_init(0);

			// we have several cases of update we need to do
			// *** done --- to do
			// F -----------------------L m1=0; m2=0 ; m3=0 ---> F - L
			// F -----------------m1****L m1>F; m2 < F ---> F-m1
			// F -----------m1****m3----L m1>F; m3<L ---> F-m1, m3-L
			// F ******m2----m1****m3---L m2<m1,m2>F  ---> m2-m1, m3-L
			if (gmp_cmp($m1, $first) > 0 ) {
				if (gmp_cmp ($m2, $first) > 0 && gmp_cmp($m2, $m1) < 0) {
					// case m2-m1
					$first1 = $m2;
					$last1 = $m1;
				} else {
					//case F-m1 or F-L
					$first1 = $first;
					$last1 = gmp_min($m1, $last);
				}
			}
			if (gmp_cmp($m3, $last) < 0) {
				// case m3-L
				$first2 = gmp_max($first, $m3);
				$last2 = $last;
			}
//            if ($config['update_disable_keys'])
  //              $this->db->toggle_index("parts_$groupID", FALSE);
			try {
				$total_count = gmp_init(0);
				$total = gmp_add(gmp_sub($last1, $first1), gmp_sub($last2, $first2));
				if (gmp_cmp($last1, 0) > 0 && gmp_cmp($first1, 0) > 0)
					$total_count = $this->get_headers($groupArr, $first1, $last1, $item, $mindate, $total, 0, FALSE, $total_max, gmp_init(0), $total_correction, $compressed_headers);
				if (gmp_cmp($total_max, 0) == 0 || gmp_cmp($total_count, $total_max) <= 0)
                    $this->get_headers($groupArr, $first2, $last2, $item, $mindate, $total, gmp_sub($last1, $first1), TRUE, $total_max, $total_count, $total_correction, $compressed_headers);
                $f1 = gmp_strval($first);
                $l1 = gmp_strval($last);
                if ($continue_old === FALSE) {
                    $query = "UPDATE groups SET \"last_updated\" = '" . time() . "' , \"mid_record\"='$f1', \"first_record\" = '$f1', \"last_record\" = '$l1' WHERE \"ID\" = $groupID";
                    $res = $this->db->execute_query($query);
                }
			}
			catch (exception $e) {
            //    if ($config['update_disable_keys'])
            //        $this->db->toggle_index("parts_$groupID", TRUE);
				throw $e;
			}
           // if ($config['update_disable_keys'])
           //     $this->db->toggle_index("parts_$groupID", TRUE);
			return TRUE;
		} catch (exception $e) { 
			write_log("Error while updating {$groupArr['name']}: ". $e->getMessage() . ' (' . $e->getCode(). ')', LOG_ERR);
			if ($e->getCode() !== NNTP_NOT_CONNECTED_ERROR) {
				echo_debug_trace($e, DEBUG_NNTP);
				update_queue_status ($this->db, $dbid, QUEUE_FAILED, 0, 100, $e->getMessage());
			}
            return $e->getCode(); 
		}
	}
	private function get_overview_format()
	{
		/*
		XoverFormat: (for newszilla.xs4all.nl)
		Array
		(
		    [Subject] =>
		    [From] =>
		    [Date] =>
		    [Message-ID] =>
		    [References] =>
		    [:bytes] =>
		    [:lines] =>
		    [Xref] => 1
		)
		 */
/*
		// getOverviewFormat gives back fixed values for the first 7 items!!! Useless!
		// Should fix getOverviewFormat to parse the LIST OVERVIEW.FMT response!


		$this->xoverformat = $this->nntp->getOverviewFormat(TRUE, TRUE);
		$temp = array_keys($this->xoverformat);
		$this->xover_subject	= array_search('Subject',$temp);
		$this->xover_from	= array_search('From',$temp);
		$this->xover_date	= array_search('Date',$temp);
		$this->xover_messageid	= array_search('Message-ID',$temp);
		$this->xover_bytes	= array_search('Bytes',$temp);
		$this->xover_lines	= array_search('Lines',$temp);
		$this->xover_xref	= array_search('Xref',$temp);

		// Check if we found every required value:
		if ($this->xover_subject == FALSE ||
			$this->xover_from == FALSE ||
			$this->xover_date == FALSE ||
			$this->xover_messageid == FALSE ||
			$this->xover_bytes == FALSE)
			throw new exception("Failed to find a required XOver format value");

		// First value is always number, shift rest:
		$this->xover_number = 0;
		$this->xover_subject++;
		$this->xover_from++;
		$this->xover_date++;
		$this->xover_messageid++;
		$this->xover_bytes++;
		$this->xover_lines++;
		$this->xover_xref++;
 */
		$this->xover_number	= 0;
		$this->xover_subject	= 1;
		$this->xover_from	= 2;
		$this->xover_date	= 3;
		$this->xover_messageid	= 4;
		$this->xover_bytes	= 6;
		$this->xover_lines	= 7;
		$this->xover_xref	= 8;
	}

	private function parse_messages(array $msgs, $mindate, &$older_counter)
	{
		// Parse messages received from a newsgroup and store it in the database

		// From the parts table, a binaries table will be generated at the end, from which setdata will be generated.
		// This is quicker than updating them on the fly.

		/*
		After explode on \t:
		Array
		(
		    [0] => 51368878
		    [1] => Superman.Boxset.Multi.Pal.DVDR REPOST---WWW.UNITED-FORUMS.CO.UK---[158/416] -Superman2.Multi.Pal.--WWW.UNITED-FORUMS.CO.UK--.r53 (128/201)
		    [2] => web@www.united-forums.co.uk (FiSO)
		    [3] => Tue, 26 Feb 2008 13:21:40 GMT
		    [4] => <part128of201.EFPVc$UyVk20CXtrwBhA@powerpost2000AA.local>
		    [5] =>
		    [6] => 258900
		    [7] => 1986
		    [8] => Xref: article.news.xs4all.nl alt.binaries.boneless:1245016480 alt.binaries.dvd:419435634 alt.binaries.ftd:35220227 alt.binaries.movies:51368878
		)
		 */

		$allParts = array();
        $cnt = 0;
        if ($mindate < 0)
            $mindate = 0;
        $binary_id = array();

		foreach ($msgs as $msgtxt) {
			// Split the bunch:
			$msg = explode("\t", $msgtxt);
			if (!isset($msg[$this->xover_subject])) continue;
			if (!isset($msg[$this->xover_bytes])) continue;
			if (!isset($msg[$this->xover_messageid])) continue;
			if (!isset($msg[$this->xover_date])) continue;
			if (!isset($msg[$this->xover_from])) continue;
			$date		= strtotime($msg[$this->xover_date]);

			if ($date < $mindate && $date > 0) {
				++$cnt;
				continue;
            }
			$bytes		= $msg[$this->xover_bytes];
            if ($bytes <= 0 || $date <= 0) continue;
			$from		= $msg[$this->xover_from];
			$messageID	= $msg[$this->xover_messageid];
			$subject	= $msg[$this->xover_subject];

			// pregmatch to find article numbers and such:
			if (!preg_match('|^(.*)\((\d+)/(\d+)\)|', $subject, $vars)) 
				continue;
			if (!isset($vars[2])) continue; // it probably isn't a binary file but a regular post... don't add it 
			$subject = trim($vars[1]);
			if ($subject == '') continue;
			$part0 = $vars[2];
			if (!is_numeric($part0)) continue;
			// Create binaryID / add to store of tables:
			$table = new TableParts;
            $table->binaryID	= create_binary_id($subject, $from);
			$table->messageID	= substr($messageID, 1, -1);
			$table->partnumber	= (int)$part0;
			$table->size		= $bytes;
            // Yes, these are not 3NF, but they save executing 2 extra queries:
            if (in_array($table->binaryID, $binary_id)) {
                $table->fromname = '';
                $table->subject = '';
            } else {
                $table->fromname = utf8_encode(trim($from));
                $table->subject	= utf8_encode($subject);
                if (count($binary_id) >= BINARYID_CACHE_SIZE)
                    array_shift($binary_id);
                $binary_id[] = $table->binaryID;
            }
            $table->date = $date;

            $allParts[] = $table;
        }
        // Add all parts into the database:
        if ($cnt == 0)
            $older_counter = 0;
        else
            $older_counter += $cnt;
        $this->db->add_parts($allParts, $this->groupID);
        return TRUE;
    }

    private function store_ETA_info($timeneeded, $nrmessages, $articlesdone, $totalarticles, $dbid)
    {
        assert(is_numeric($timeneeded) && is_numeric($nrmessages) && is_numeric($articlesdone) && is_numeric($totalarticles) && is_numeric($dbid));
        // Sanity check:
        if ($timeneeded == 0) 
            return;

        $downloadspeed = $nrmessages / $timeneeded;

        if(round($downloadspeed) > 0) {
            // Update $this->downloadspeedArr:
            $this->downloadspeedArr[] = $downloadspeed;
            if (count($this->downloadspeedArr) > 10) {
                // Remove oldest value:
                array_shift($this->downloadspeedArr);
            }
        }

        // Basic downloadspeed: Average of all values in dowlnoadspeedArr:
        $avgdownloadspeed = array_sum($this->downloadspeedArr) / count($this->downloadspeedArr);

        // ETA based on weighted average:
        if (count($this->downloadspeedArr) < 3) {
            // Average of all items
            $weightedspeed = $avgdownloadspeed;
        } else {
            // Weighted average over last items
            $max = count($this->downloadspeedArr);
            $a = $this->downloadspeedArr[$max-1]; // last
            $b = $this->downloadspeedArr[$max-2]; // second to last
            $c = $this->downloadspeedArr[$max-3]; // third to last

            $weightedspeed = (0.5 * $a) + (0.3 * $b) + (0.2 * $c);
        }

        $weightedspeed = floor($weightedspeed);
        if ($weightedspeed == 0) 
            $weightedspeed = 0.01;

        // Calculate ETA:
        $percentage = ($totalarticles > 0) ? floor(100 * ($articlesdone / $totalarticles)):0;
        $percentage = min(100, $percentage); // Just in case ;)
        $articlestodo = $totalarticles - $articlesdone;
        $ETA = round($articlestodo / $weightedspeed);

        // Store:
        store_ETA($this->db, $ETA, $percentage, $weightedspeed . ' articles/s - ' . "total $totalarticles articles" , $dbid);
    }
    private function select_group_name($name, &$code)
    {
        try {
            $data = $this->nntp->select_group($name);
        } catch (exception $e) {
            $code = $e->getcode();
            if ($code == NNTP_PROTOCOL_RESPONSECODE_AUTHENTICATION_REQUIRED)
                throw new exception("Could not select group: $name. Authentication needed.", ERR_GROUP_NOT_FOUND);
            else 
            throw new exception("Could not select group: $name", ERR_GROUP_NOT_FOUND);
        }
        return $data;
    }

    function select_group($groupid, &$code)
    {
        echo_debug_function(DEBUG_NNTP, __FUNCTION__);
        if (is_numeric($groupid)) {
            $name = group_name($this->db, $groupid);
        } else
            $name = $groupid;
        $code = 0;

        try {
            $data = $this->nntp->select_group($name);
        } catch (exception $e) {
            $code = $e->getcode();
            if ($code == NNTP_PROTOCOL_RESPONSECODE_AUTHENTICATION_REQUIRED)
                throw new exception("Could not select group: $groupid. Authentication needed.", ERR_GROUP_NOT_FOUND);
            else 
                throw new exception("Could not select group: $groupid", ERR_GROUP_NOT_FOUND);
        }
        return $data;
    }

    function update_group_list($expire_time) 
    {
        assert(is_numeric($expire_time));
		global $config;
		echo_debug_function(DEBUG_NNTP, __FUNCTION__);
		write_log('Getting grouplist from server.', LOG_NOTICE);
		if (isset($config['group_filter']))
			$pattern = $config['group_filter'];
		else 
			$pattern = '';
		try {
			$groups = $this->nntp->get_groups($pattern);
		} catch (exception $e) {
			throw new exception('Could not update group list',ERR_NNTP_CONNECT_FAILED);
		}

		echo_debug('Processing grouplist.', DEBUG_NNTP);
		$update = $insert = 0;

		// New Function => Cache all known groups in one array
		$res = $this->db->execute_query('SELECT "ID", "name" FROM groups');

		// Filling the Array 
		if (is_array($res))
			foreach ($res as $dbGroup)
				$grouparray[$dbGroup['name']] = $dbGroup['ID']; 

		foreach($groups as $group) {
			if(isset ($group['group']) && stristr($group['group'], 'alt.bin')) {
				$gr = $group['group'];

				$desc = (isset($group['desc'])) ? $group['desc'] : '';
				$msg_count = $group['last'] - $group['first'];
				if ($msg_count < 0) $msg_count = 0;

				if(isset($grouparray["$gr"])) {
					$ID = $grouparray["$gr"];
					$this->db->escape($ID, TRUE);
					$this->db->escape($desc, TRUE);
					$query = "UPDATE groups SET \"description\" = $desc, \"postcount\"= '$msg_count' WHERE \"ID\" = $ID";
					$update ++;
					$this->db->execute_query($query);
				} else {
					$rows = array ('name', 'description', 'active', 'expire', 'postcount');
					$vals = array ($gr, $desc, 0, $expire_time, $msg_count);
					$this->db->insert_query('groups', $rows, $vals);
					$insert ++;
				}
			}
		} 
		write_log('Done with grouplist from server.'. " Inserted $insert, Updated $update", LOG_NOTICE);

		return array($update, $insert);
	}
	function test_nntp($group, &$code)
	{
		$code = 0;
		try {
			$data = $this->select_group_name($group, $code);
			$last = $data['last'];
			$msgs = $this->nntp->get_fast_overview($last - 2,  $last, FALSE);
			if (count($msgs) == 0)
				return FALSE;
			return TRUE;
		} catch (exception $e) {
			return FALSE;
		}
    }
    function test_compressed_headers_nntp($group, &$code)
    {
		$code = 0;
        try {
            $data = $this->select_group_name($group, $code);
            $last = $data['last'];
            $this->nntp->set_compressed_headers(TRUE);

            $msgs = $this->nntp->get_fast_overview($last - 2,  $last);
            if (stristr($msgs[0], 'ybegin'))
                return TRUE;
            return FALSE;
            
        } catch (exception $e) {
			return FALSE;
		}
        
    }
    function server_needs_auth($group)
    {
	    try {
		    $data = $this->select_group_name($group, $code);
		    return FALSE;
	    } catch(exception $e) {
		    if ($code == NNTP_PROTOCOL_RESPONSECODE_AUTHENTICATION_REQUIRED)
			    return TRUE;
		    return FALSE;
	    }
    }
    function post_article(array $article)
    { 
        $this->nntp->post($article, $articleid);
        return $articleid;
    }


} //end class


?>
